////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    ElLuchadorGuts.cpp											  //
//  Developer:																  //	
//  Create date:                                                              //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called El Luchador.			      //
//					(client provided by Genesis)							  //	
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        2010 - Riplife Gaming.  All rights reserved.						  //
////////////////////////////////////////////////////////////////////////////////

#include "ElLuchadorGuts.h"
#include "slotconn.h"
#include "sltsql.h"


ElLuchadorGuts::SpinResult ElLuchadorGuts::m_aLineWinSpinResults[][6] = 
{
// Number of icons:			0			1						2					3					4					5
/* ICON_WILD */			{	EVT_LOSER,	EVT_LOSER,				EVT_TWO_WILD,		EVT_THREE_WILD,		EVT_FOUR_WILD,		EVT_FIVE_WILD		},
/* ICON_FIGHTER1 */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_THREE_FIGHTER1,	EVT_FOUR_FIGHTER1,	EVT_FIVE_FIGHTER1	},
/* ICON_FIGHTER2 */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_THREE_FIGHTER2,	EVT_FOUR_FIGHTER2,	EVT_FIVE_FIGHTER2	},
/* ICON_FIGHTER3 */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_THREE_FIGHTER3,	EVT_FOUR_FIGHTER3,	EVT_FIVE_FIGHTER3	},
/* ICON_RABBIT */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_THREE_RABBIT,	EVT_FOUR_RABBIT,	EVT_FIVE_RABBIT		},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_THREE_ACE,		EVT_FOUR_ACE,		EVT_FIVE_ACE		},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_THREE_KING,		EVT_FOUR_KING,		EVT_FIVE_KING		},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_THREE_QUEEN,	EVT_FOUR_QUEEN,		EVT_FIVE_QUEEN		},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_THREE_JACK,		EVT_FOUR_JACK,		EVT_FIVE_JACK		},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_THREE_TEN,		EVT_FOUR_TEN,		EVT_FIVE_TEN		},
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,				EVT_TWO_NINE,		EVT_THREE_NINE,		EVT_FOUR_NINE,		EVT_FIVE_NINE		},
/* ICON_SCATTER */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			}
};


////////////////////////////////////////////////////////////////////////////
/// ElLuchadorGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Scatter, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_SCATTER ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_SCATTER.
////////////////////////////////////////////////////////////////////////////
ElLuchadorGuts::SpinResult ElLuchadorGuts::m_aScatterWinSpinResults[][6] = 
{
// Number of icons:			0			1						2					3					4					5
/* ICON_WILD */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_FIGHTER1 */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_FIGHTER2 */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_FIGHTER3 */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_RABBIT */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_SCATTER */		{	EVT_LOSER,	EVT_SCATTER_NEAR_MISS,	EVT_TWO_SCATTER,	EVT_THREE_SCATTER,	EVT_FOUR_SCATTER,	EVT_FIVE_SCATTER	}
};

////////////////////////////////////////////////////////////////////////////
/// ElLuchadorGuts::m_aPayLineOffsets
///
/// Declare an array to look up the offset for a reel for each payline.
/// An offset of 0 means that the reel should stop on the middle row,
/// an offset of 1 means the reel should stop on the top row,
/// and an offset of -1 means the reel should stop on the bottom row.
/// So the offset is relative to the middle row, or pay line 1.
////////////////////////////////////////////////////////////////////////////
int8 ElLuchadorGuts::m_aPayLineOffsets[25][5] = 
{
	{  0,  0,  0,  0,  0 },	// Pay line 1
	{  1,  1,  1,  1,  1 },	// Pay line 2
	{ -1, -1, -1, -1, -1 },	// Pay line 3
	{  1,  0, -1,  0,  1 },	// Pay line 4
	{ -1,  0,  1,  0, -1 },	// Pay line 5
	{  1,  1,  0, -1, -1 },	// Pay line 6
	{ -1, -1,  0,  1,  1 },	// Pay line 7
	{  0,  1,  0, -1,  0 },	// Pay line 8
	{  0, -1,  0,  1,  0 },	// Pay line 9
	{  1,  0,  0,  0, -1 },	// Pay line 10
	{ -1,  0,  0,  0,  1 },	// Pay line 11
	{  0,  1,  1,  0, -1 },	// Pay line 12
	{  0, -1, -1,  0,  1 },	// Pay line 13
	{  0,  0,  1,  0, -1 },	// Pay line 14
	{  0,  0, -1,  0,  1 },	// Pay line 15
	{  1,  1,  0, -1,  0 },	// Pay line 16
	{ -1, -1,  0,  1,  0 },	// Pay line 17
	{  0,  1,  0, -1, -1 },	// Pay line 18
	{  0, -1,  0,  1,  1 },	// Pay line 19
	{  1,  1,  1,  0, -1 },	// Pay line 20
	{ -1, -1, -1,  0,  1 },	// Pay line 21
	{  1,  0, -1, -1, -1 },	// Pay line 22
	{ -1,  0,  1,  1,  1 },	// Pay line 23
	{  1,  0,  1,  0,  1 },	// Pay line 24
	{ -1,  0, -1,  0, -1 }	// Pay line 25
};


////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::ElLuchadorGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
ElLuchadorGuts::ElLuchadorGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aScatterWinSpinResults array. 
	C_ASSERT( countof( m_aScatterWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::~ElLuchadorGuts()
///
/// Destructor for ElLuchadorGuts. 
////////////////////////////////////////////////////////////////////////////
ElLuchadorGuts::~ElLuchadorGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::getScatterWinResult()   
///
/// @param   spin				The current spin object.
/// @param   testIcon			The scatter icon that we are searching for.
/// @param   scatterPositions	The bit mask for the scatters positions that we find.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
/// This helper method is used to evaluate the spin for scatters.
////////////////////////////////////////////////////////////////////////////
ElLuchadorGuts::SpinResult ElLuchadorGuts::getScatterWinResult( SlotSpin* spin, uint8 testIcon, int32& scatterPositions ) const
{
	// Declare a local variable to keep up with the icon count.
	uint8 numScatterIcons = 0;

	// Initialize the scatterPositions.
	scatterPositions = 0;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is the one we are looking for...
			if ( currentIcon == testIcon )
			{
				// then increase the icon count
				numScatterIcons++;

				// Set the bit in the position mask for this stop.
				scatterPositions |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Use the array to look up the spin result for numScatterIcons.
	return lookupScatterWinSpinResult( testIcon, numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::evaluateScatterWin()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void ElLuchadorGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions )
{
	// If we got the scatter win (or almost got it)
	if ( srScatterResult != EVT_LOSER )
	{
		// Free games multiplier?
		uint8 freeGamesMultiplier = getGameModeId() == MODE_FREE_SPIN ? FREE_GAME_MULTIPLIER : 1;

		// Feature multiplier?
		uint8 iFeatMultiplier = m_subGame->getData( SGDIDX_MULTIPLIER_FEATURE );
		
		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;

		// Add the payout (with multiplier and bet per line) to the total win for this spin.
		increaseTotalWin( nPayout * getBetPerLine() * getNumLinesBet() * iFeatMultiplier * freeGamesMultiplier );
    
		// If we have more than three scatters, free spins awarded
		int nFreeSpinsAwarded = 0;
		if (srScatterResult == EVT_THREE_SCATTER ||  srScatterResult == EVT_FOUR_SCATTER || srScatterResult == EVT_FIVE_SCATTER)
		{
			nFreeSpinsAwarded = 10;
		}

		// Add a scatter to the spin. Apply FeatMultiplier if neccessary
		SlotScatter* pScatter = spin->addScatter( scatterPositions, nPayout * getBetPerLine() * getNumLinesBet(), 
			                                      iFeatMultiplier*freeGamesMultiplier, false, srScatterResult, nFreeSpinsAwarded );
		if (iFeatMultiplier > 1)
		{
			pScatter->addNote( NOTE_GAME_MULTIPLIER );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::classifyLuchadorScatters( SlotSpin* spin, uint8 FeatMultiplier )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
///
/// Returns: scatter positions (binary mask)
////////////////////////////////////////////////////////////////////////////
int32 ElLuchadorGuts::classifyLuchadorScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Declare a local variable to track the scatter positions.
	int32 nScatterPositions = 0;

	// See if there is a scatter.
	SpinResult srResult = getScatterWinResult( spin, ICON_SCATTER, nScatterPositions );

	// Add the scatter, if any.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	//  If three or more scatters, activate free spins
	if (srResult == EVT_THREE_SCATTER ||  srResult == EVT_FOUR_SCATTER || srResult == EVT_FIVE_SCATTER)
	{
		incrementFreeSpins( 10 );
		setGameModeId( MODE_FREE_SPIN );
	}

	return nScatterPositions;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 ElLuchadorGuts::isValidGuts()
{
	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	ASSERT( countof( m_aScatterWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range. GENESIS GAMES: [9000,9500)
    return ( getGutsId() >= 9000 && getGutsId() < 9500 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param   nGameModeMultiplier	The multiplier for the game mode.
/// @param   nNoteId				The note ID for the game mode.
///
/// This helper method is used to get the game mode multiplier and note ID.
////////////////////////////////////////////////////////////////////////////
void ElLuchadorGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier,
	uint8& nNoteId )
{
	// If we are in free spin mode...
    if ( getGameModeId() == MODE_FREE_SPIN )
    {
		
    }
	// Otherwise...
    else
    {
		// just defer to the base class.
        SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden when running in debug spins mode to alter the reel stops.
////////////////////////////////////////////////////////////////////////////
void ElLuchadorGuts::preSpinClassifyResults( SlotSpin* spin )
{
	bDebugAddWilds = false;
	bDebugMultiplier = false;
	bDebugCurtain = false;

	SlotGuts::preSpinClassifyResults(spin);

	resetSubGameValues();

	// Multiplier feature?
	evaluateMultiplierFeature();

	// Added Wilds feature?
	evaluateAddedWildsFeature();
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::postSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden to see if we can complete the game.
////////////////////////////////////////////////////////////////////////////
void ElLuchadorGuts::postSpinClassifyResults( SlotSpin* spin )
{
	// Curtain pick feature?
	uint8 iTriggered = evaluateCurtainPickFeature();
	if (iTriggered)
	{
		setGameCanComplete( false );
		setBonusGameState( cpTriggered );		  // Curtain Pick Feature
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::resetFreeSpinsValues()
///
/// This method is used to initialize flags for free spins feature
////////////////////////////////////////////////////////////////////////////
void ElLuchadorGuts::resetSubGameValues()
{
	m_subGame->update( SGDIDX_MULTIPLIER_FEATURE, 0);
	m_subGame->update( SGDIDX_ADDED_WILDS_SUBST_R1, 0);
	m_subGame->update( SGDIDX_ADDED_WILDS_PAYOUT_R1, 0);
	m_subGame->update( SGDIDX_ADDED_WILDS_SUBST_R2, 0);
	m_subGame->update( SGDIDX_ADDED_WILDS_PAYOUT_R2, 0);
	m_subGame->update( SGDIDX_ADDED_WILDS_SUBST_R3, 0);
	m_subGame->update( SGDIDX_ADDED_WILDS_PAYOUT_R3, 0);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::processClientData()
///
/// @param   bonusGameType      The current type of bonus game.
/// @param   bonusDataCount     The number of integers passed from the client.
/// @param   bonusData			An array of integers passed from the client.
/// 
/// This is an override of the framework method.
/// 
////////////////////////////////////////////////////////////////////////////
bool8
ElLuchadorGuts::processClientData( uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData )
{
	// Start by assuming that we do not need to reply to this message
	bool8 bReply = false;

	// The message we are expecting from the client is a curtain number
	// So we have to check two things: are we expecting the message? is it a valid curtain what we are receiving?
	if ( m_subGame->getData(SGIDX_BONUS_GAME_STATE) == cpTriggered )
	{
		if (bonusDataCount == 1 && bonusData != NULL)
		{
			// Get curtain prize
			uint8 nCurtainMultiplier = getRandomValueFromProbabilityTable( CURTAIN_FEATURE_PRIZE );
			// Multipliers?
			if ( getGameModeId() == MODE_FREE_SPIN )
			{
				nCurtainMultiplier *= FREE_GAME_MULTIPLIER;
			}
			// Feature multiplier?
			uint8 iFeatMultiplier = m_subGame->getData( SGDIDX_MULTIPLIER_FEATURE );
			if (iFeatMultiplier > 1)
			{
				nCurtainMultiplier *= iFeatMultiplier;
			}
			
			// Add bonus (total bet * Multiplier curtain feature)
			addElLuchadorBonusGame(CURTAIN_FEATURE_BONUS, 0, getBetPerLine()*getNumLinesBet(), nCurtainMultiplier, getBetPerLine()*getNumLinesBet()*nCurtainMultiplier, 0);

			// Complete game
			setGameCanComplete( true );
			m_subGame->update( SGIDX_BONUS_GAME_STATE, cpNone );
			bReply = true;
			//}
		}
	}
 	
	return bReply;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::hasSavedGames()
///
/// @param   bool8   True/False indicating if this machine supports saved games.
///
/// This is a framework override to determine if this machine supports saved games.
////////////////////////////////////////////////////////////////////////////
bool8 ElLuchadorGuts::hasSavedGames()
{
	// We support saved games so always return true.
	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// Base class override, returns a losing spin triggered by the debug tool
////////////////////////////////////////////////////////////////////////////
void ElLuchadorGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=0;
	pStops[1]=0;
	pStops[2]=0;
	pStops[3]=0;
	pStops[4]=0;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::getProgressiveValue( )
///
/// Returns current jackpot value for HydroHeat guts
////////////////////////////////////////////////////////////////////////////
int32 ElLuchadorGuts::getProgressiveValue()
{
   SlotsSQL *slotTracker = (SlotsSQL *) tracker;
   int32 jackpot = 0;

    if ( tracker )
    {
    	slotTracker->lock();
		slotTracker->getJackpot(getJackpotId(), jackpot, getInfo()->forMoney, 1);
    	slotTracker->unlock();
    }

	return jackpot;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::getProgressiveValue( )
///
/// Base class override, returns true if bonus game is in progress
///
/// In HydroHeat we are just expecting a bonus message (user choosing a boat) 
/// if BonusState is 1 (ptgTriggered)
/// (If BonusState is 2 the free spins feature has been 
///  retriggered, but the user does not have to choose a boat again)
////////////////////////////////////////////////////////////////////////////
bool8 ElLuchadorGuts::isBonusGameInProgress()
{
	int32 nBonusGameState = getBonusGameState();

	return (bool8)( nBonusGameState == cpTriggered );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 int32 ElLuchadorGuts::addElLuchadorBonusGame 
///
/// This method is used to store a bonus game in the database. Similar to
/// the addBonusGame in slotsguts.cpp but this is a special one for all pays
/// slots, as we don't have to divide by bet per line
////////////////////////////////////////////////////////////////////////////
BonusGame*
ElLuchadorGuts::addElLuchadorBonusGame(uint8 bonusgametype,uint8 bonusgameindex,int32 basepayoutamount,int32 totalmultiplier,int32 totalbonuspayoutamount,int32 freespins,int64 overridegamenum,bool8 bSaved)
{
	int32 nPayout = totalbonuspayoutamount;

	if ( getBetPerLine() > 0 )
	{
		increaseTotalWin(nPayout);
	}

	SlotSpin* spin = getSlotSpin();
	// Include bonus game 
	return spin->addBonusGame(bonusgametype, bonusgameindex, basepayoutamount, totalmultiplier, totalbonuspayoutamount, freespins, overridegamenum);

}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 int32 ElLuchadorGuts::evaluateMultiplierFeature 
///
/// This method is used to evaluate feature multiplier in a spin
/// If no feature multiplier applied, return 1
////////////////////////////////////////////////////////////////////////////
uint8 ElLuchadorGuts::evaluateMultiplierFeature()
{
	uint8 iMultiplier = 1;
	
	if (m_bDebugSpinsMode)
	{
		if (bDebugMultiplier)
		{
			iMultiplier = getRandomValueFromProbabilityTable( MULTIPLIER_FEATURE_PRIZE );
		}
	}
	else
	{
		if ( getRandomValueFromProbabilityTable( MULTIPLIER_FEATURE ) == 1)
		{
			iMultiplier = getRandomValueFromProbabilityTable( MULTIPLIER_FEATURE_PRIZE );
		}
	}

	m_subGame->update(SGDIDX_MULTIPLIER_FEATURE, iMultiplier);

	return iMultiplier;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 int32 ElLuchadorGuts::evaluateCurtainPickFeature 
///
/// This method is used to evaluate if curtain feature is triggered
////////////////////////////////////////////////////////////////////////////
uint8 ElLuchadorGuts::evaluateCurtainPickFeature()
{
	uint8 iReturn = 0;
	if (m_bDebugSpinsMode)
	{
		if (bDebugCurtain)
		{
			iReturn = 1;
		}
	}
	else
	{
		iReturn = getRandomValueFromProbabilityTable( CURTAIN_FEATURE );
	}

	return iReturn;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 int32 ElLuchadorGuts::EvaluateAddedWilds()
///
/// This method is used to evaluate if added wilds feature is triggered
////////////////////////////////////////////////////////////////////////////
int32 ElLuchadorGuts::evaluateAddedWildsFeature()
{
	int32 iWilds = 0;
	int32 iRoundWilds = 0;
	uint8 iNumWilds = 0;
	
	// Added wilds in round 1?
	if (m_bDebugSpinsMode)
	{
		if (bDebugAddWilds)
		{
			iNumWilds = getRandomLessThan(3) + 1;
		}
		else
		{
			iNumWilds = 0;
		}
	}
	else
	{
		iNumWilds = getRandomValueFromProbabilityTable( ADDED_WILDS_R1 );
	}
	if (iNumWilds > 0)
	{
		iRoundWilds = getRoundWildsMask(iWilds, iNumWilds);
		m_subGame->update(SGDIDX_ADDED_WILDS_SUBST_R1, iRoundWilds);
		iWilds |= iRoundWilds;

		// Added wilds in round 2?
		iNumWilds = getRandomValueFromProbabilityTable( ADDED_WILDS_R2 );
		if (iNumWilds > 0)
		{
			iRoundWilds = getRoundWildsMask(iWilds, iNumWilds);
			m_subGame->update(SGDIDX_ADDED_WILDS_SUBST_R2, iRoundWilds);
			iWilds |= iRoundWilds;

			// Added wilds in round 3?
			iNumWilds = getRandomValueFromProbabilityTable( ADDED_WILDS_R3 );
			if (iNumWilds > 0)
			{
				iRoundWilds = getRoundWildsMask(iWilds, iNumWilds);
				m_subGame->update(SGDIDX_ADDED_WILDS_SUBST_R3, iRoundWilds);
				iWilds |= iRoundWilds;
			}
		}
	}

	return iWilds;
}

int32 ElLuchadorGuts::getRoundWildsMask(int32 WildsMask, uint8 NumWilds)
{
	int32 iRoundWilds = 0;
	
	while ((NumWilds--) > 0)
	{
		bool bSameWild = true;
		do
		{
			int32 iAddedWildPosition = 1 << (getrand() % 14);
			// We cannot add a wild that was already in the mask
			if ((WildsMask & iAddedWildPosition) == 0)
			{
				iRoundWilds |= iAddedWildPosition;
				bSameWild = false;
			}
		} while (bSameWild);
	}
	
	return iRoundWilds;
}

void ElLuchadorGuts::classifyResult(SlotSpin *spin)
{
	uint8 *classifyPositions = new uint8[spin->getStopCount()];
    uint8 result = 0;

	// First of all, before evaluating lines, classify scatters
	// Classify any scatter pays (implemented as a null function in the base)
	spin->clearScatters();
	int32 nScatterPositions = classifyLuchadorScatters(spin);

	// We can have more that a prize per line in El Luchador (Added Wilds feature), so we are going to create new lines 
	// in order to support this.
	// Lines 1-25	-> normal round, no added wilds		
	// Lines 26-50	-> added wilds round 1 (wilds round 1 applied)
	// Lines 51-75	-> added wilds round 2 (wilds round 1,2 applied)
	// Lines 76-100	-> added wilds round 3 (wilds round 1,2,3 applied)
	// We have just created lines from 1 to 25, so we have to create the rest of them:
	
	int32 iWildsRound1 = 0;
	int32 iWildsRound2 = 0;
	int32 iWildsRound3 = 0;
	// Added wilds in round 1?
	iWildsRound1 = m_subGame->getData( SGDIDX_ADDED_WILDS_SUBST_R1 );
	if (iWildsRound1 > 0)
	{
		// In added wilds round, scatter positions become also wilds
		if (nScatterPositions > 0)
		{
			iWildsRound1 |= nScatterPositions;
			m_subGame->update(SGDIDX_ADDED_WILDS_SUBST_R1, iWildsRound1);
		}

		for ( uint8 i = getNumLinesBet() + 1; i <= 25; i++)
		{
			spin->addLine(i);
		}
		for ( uint8 i = 26; i <= 50; i++ )
		{
			spin->addLine(i);
		}

		// Added wilds in round 2?
		iWildsRound2 = m_subGame->getData( SGDIDX_ADDED_WILDS_SUBST_R2 );
		if (iWildsRound2 > 0)
		{
			// In added wilds round, scatter positions become also wilds
			if (nScatterPositions > 0)
			{
				iWildsRound2 |= nScatterPositions;
				m_subGame->update(SGDIDX_ADDED_WILDS_SUBST_R2, iWildsRound2);
			}

			for ( uint8 i = 51; i <= 75; i++ )
			{
				spin->addLine(i);
			}

			// Added wilds in round 3?
			iWildsRound3 = m_subGame->getData( SGDIDX_ADDED_WILDS_SUBST_R3 );
			if (iWildsRound3 > 0)
			{
				// In added wilds round, scatter positions become also wilds
				if (nScatterPositions > 0)
				{
					iWildsRound3 |= nScatterPositions;
					m_subGame->update(SGDIDX_ADDED_WILDS_SUBST_R3, iWildsRound3);
				}

				for ( uint8 i = 76; i <= 100; i++ )
				{
					spin->addLine(i);
				}
			}
		}
	}
	
    /**
     * Go through the played paylines one at a time looking for winners.
     */
	int iAddedWildsMask = 0;
	uint8 iLineAux = 0;
	int8 iNoteAddedWilds = 0;
    for ( uint8 lineidx = 0; lineidx < spin->getSpinLineCount(); lineidx++, iLineAux++ )
    {
		SpinLine *spinLine = spin->getSpinLineAt(lineidx);

		// Apply Wild masks from Added Wilds feature
		if (lineidx == 25)
		{
			m_subGame->update(SGDIDX_ADDED_WILDS_PAYOUT_R1, getTotalWin());
			iAddedWildsMask |= iWildsRound1;
			iLineAux = 0;
			iNoteAddedWilds = NOTE_ADDED_WILDS_R1;
		}
		if (lineidx == 50)
		{
			m_subGame->update(SGDIDX_ADDED_WILDS_PAYOUT_R2, getTotalWin());
			iAddedWildsMask |= iWildsRound2;
			iLineAux = 0;
			iNoteAddedWilds = NOTE_ADDED_WILDS_R2;

		}
		if (lineidx == 75)
		{
			m_subGame->update(SGDIDX_ADDED_WILDS_PAYOUT_R3, getTotalWin());
			iAddedWildsMask |= iWildsRound3;
			iLineAux = 0;
			iNoteAddedWilds = NOTE_ADDED_WILDS_R3;
		}

		// Just evaluate number of lines bet
		if (iLineAux >= getNumLinesBet())
		{
			continue;
		}
		
        /**
         * Iterate the stops to get the classify positions.
         */
		uint8 iLineId = (spinLine->lineid == 25 || spinLine->lineid == 50 || spinLine->lineid == 75 || spinLine->lineid == 100) ? 25 : spinLine->lineid % 25;
        for ( uint8 stopid = 0; stopid < spin->getStopCount(); stopid++ )
        {
            uint8 offsetPosition = getPayline(iLineId, stopid)->wheelposition;
            /**
             * Calculate the offset so we are using the proper icon
             * for this payline.
             */
            classifyPositions[stopid] = relativeWheelPosition(getGameModeId(), stopid, spin->getStops()[stopid], offsetPosition);
        }

        classifyElLuchadorStops(spinLine, classifyPositions, spin->getBet(), iLineAux, iAddedWildsMask, result);

		uint8 iFreeSpinsMultiplier = getGameModeId() == MODE_FREE_SPIN ? FREE_GAME_MULTIPLIER : 1;
		uint8 iFeatureMultiplier = m_subGame->getData(SGDIDX_MULTIPLIER_FEATURE );

        int32 nPayout = getPayout(result)->payoutAmount;
		nPayout = nPayout * getBetPerLine();
        setTotalWin( getTotalWin() + (nPayout  * iFeatureMultiplier * iFreeSpinsMultiplier));

		/*
		spin->setPayoutAmount(spinLine->lineid,		                           // Line ID
					getPayout(result)->payoutAmount * getBetPerLine(),         // Base Payout Amount
                    multiplier,                                                // Need to calc multiplier
					nPayout,												   // Actual payout
                    false,	                                                   // Jackpot
					result);	                                               // Payouttypeid
		*/
		spinLine->basepayoutamount = nPayout;
		spinLine->totalmultiplier =  iFeatureMultiplier * iFreeSpinsMultiplier;
		spinLine->totallinepayoutamount = nPayout * iFeatureMultiplier * iFreeSpinsMultiplier;
		spinLine->jackpot = false;
		spinLine->payouttypeid = result;
		
		if (iFreeSpinsMultiplier > 1)
		{
			spinLine->addNote( NOTE_FREE_GAMES_MULTIPLIER );
		}

		if (iFeatureMultiplier > 1)
		{
			spinLine->addNote( NOTE_GAME_MULTIPLIER );
		}

		if (iNoteAddedWilds > 0)
		{
			spinLine->addNote( iNoteAddedWilds );
		}
	}

    // Generate the wheel view for reporting
    storeWheelView(spin);

    delete [] classifyPositions;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::classifyElLuchadorStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void ElLuchadorGuts::classifyElLuchadorStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line, int32 addedWilds, uint8& rtnresult )
{
	// Track the Cauldron result separately since it might be changed to a substitution.
	SpinResult srWildLineWinResult = getWildsWinResult( stops, line, addedWilds );  

	// Now see what the payout is if we use any Cauldron we find as substitution symbols.
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Maybe the first line position was substitued by a Wild
	uint8 row = getRowLineReel(line, 0);
	int32 position = 1 << (5 * row);
	// If that position is a Cauldron substitution, we have a substitution. Modify test icon
	if ((addedWilds & position) > 0)
	{
		testIcon = ICON_WILD;
	}
	
	// See what the spin result is if we used any Cauldron as substitution symbols.
	SpinResult srResultWithPossibleSubstitution =
		getLineWinResultWithPossibleSubstitution( stops, line, testIcon, addedWilds );

	// Use the largest payout between the regular Cauldron evaluation compared to
	// the evaluation with the possible substitution.
    if ( doesWildLineWinPayMore( srWildLineWinResult, srResultWithPossibleSubstitution) )
	{
		// The Cauldrons by themselves pay out more so use the Wilds result.
		rtnresult = srWildLineWinResult;
	}
	else
	{
		// Otherwise the result with the substitution (if any) paid more so use it.
		rtnresult = srResultWithPossibleSubstitution;
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElluchadorGuts::SpinResult ElLuchadorGuts::getWildsWinResult( uint8* stops, uint8 line, int32 addedWilds ) const
///
/// @param   stops			The array of stop positions for the line.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the Cauldron line win payout.
////////////////////////////////////////////////////////////////////////////
ElLuchadorGuts::SpinResult ElLuchadorGuts::getWildsWinResult( uint8* stops, uint8 line, int32 addedWilds )
{
	// Count how many Cauldrons we have at the beginning of the line.
	for ( uint8 i = 0; i < getReelCount(); i++ )
    {
		// If this icon is not a Cauldron...
		int32 iIconID = getIcon( getGameModeId(), i, stops[i] )->iconid;
		if ( iIconID != ICON_WILD )
		{
			// Maybe that icon was substitued by a cauldron
			uint8 row = getRowLineReel(line, i);
			int32 position = 1 << (row * 5 + i);
			// If that position is not a Cauldron substitution, we have finished
			if ((addedWilds & position) == 0)
			{
				break;
			}
		}
    }

	// Use the array to look up the spin result for i Cauldrons.
	return lookupLineWinSpinResult( ICON_WILD, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ElLuchadorGuts::SpinResult ElLuchadorGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon, int32 addedWilds ) const
///
/// @param   stops			The array of stop positions for the line.
/// @param   testIcon		The icon we are looking for, typically the first icon on the line.
/// @param   bSubstitution	An output parameter that is set if a subsitution is used.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout with a possible substitution.
////////////////////////////////////////////////////////////////////////////
ElLuchadorGuts::SpinResult ElLuchadorGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 line, uint8 testIcon, int32 addedWilds )
{
	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Cauldron (Wild)...
		if ( currentIcon == ICON_WILD )
		{
			// then use it as a substitution symbol and continue on.
			continue;
		}
		// If the current icon is substitued by a Cauldron (Wild)...
		else
		{
			
			uint8 row = getRowLineReel(line, i);
			int32 position = 1 << (row*5 + i);
			if ((addedWilds & position) != 0)
			{
				continue;						
			}		
		}
		
		// Otherwise if the test icon is a Cauldron...
		if ( testIcon == ICON_WILD )
		{
			// We have now found our first icon that is not a Wild.  All
			// of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			break;											
        }
    }

	// Use the array to look up the spin result for i testIcons.
  	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 bool ElLuchadorGuts::doesWildLineWinPayMore( SpinResult srWilds, SpinResult srOther ) const
///
/// @param   srWilds		The spin result to calculate the Wilds payout for.
/// @param   srOther		The spin result to calculate the other payout for.
///
/// @return	 bool			True if the Cauldron win is larger.
///
/// This helper method is used to see which payout is larger.
////////////////////////////////////////////////////////////////////////////
bool ElLuchadorGuts::doesWildLineWinPayMore( SpinResult srWilds, SpinResult srOther ) const
{
	// Lookup the payout for the Cauldron line win.
	long nWildsLineWinPayout = getPayout( srWilds )->payoutAmount;

	// Lookup the payout for the other line win.
	long nOtherLineWinPayout = getPayout( srOther )->payoutAmount;

	// Return true if the Cauldron payout is bigger.
    return ( nWildsLineWinPayout > nOtherLineWinPayout );
}

////////////////////////////////////////////////////////////////////////////
/// @fn		ElLuchadorGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void ElLuchadorGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( getNumLinesBet() ) || m_bForceRandomJackpot)
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }

	// In third party games (API) we include the jackpot in every spin so that they can update it.
	// If no jackpot, they have not won the jackpot
	setJackpotAmount( getProgressiveValue() );
}



// DEBUG FUNCTIONS
bool8 ElLuchadorGuts::bDebugAddWilds = false;
bool8 ElLuchadorGuts::bDebugMultiplier = false;
bool8 ElLuchadorGuts::bDebugCurtain = false;

void ElLuchadorGuts::initDebugFunctionMap()
{
	SlotGuts::initDebugFunctionMap();

	addDebugStopFunction( EVT_DEBUG_ADDWILDS, 
		new DebugStopFunc<ElLuchadorGuts>(this, &ElLuchadorGuts::Debug_AddWilds) );

	addDebugStopFunction( EVT_DEBUG_MULTIPLIER, 
		new DebugStopFunc<ElLuchadorGuts>(this, &ElLuchadorGuts::Debug_Multiplier) );

	addDebugStopFunction( EVT_DEBUG_CURTAIN, 
		new DebugStopFunc<ElLuchadorGuts>(this, &ElLuchadorGuts::Debug_Curtain) );

	addDebugStopFunction( EVT_DEBUG_ADDWILDS_MULTIPLIER, 
		new DebugStopFunc<ElLuchadorGuts>(this, &ElLuchadorGuts::Debug_AddWilds_Multiplier) );

	addDebugStopFunction( EVT_DEBUG_ADDWILDS_CURTAIN, 
		new DebugStopFunc<ElLuchadorGuts>(this, &ElLuchadorGuts::Debug_AddWilds_Curtain) );

	addDebugStopFunction( EVT_DEBUG_MULTIPLIER_CURTAIN, 
		new DebugStopFunc<ElLuchadorGuts>(this, &ElLuchadorGuts::Debug_Multiplier_Curtain) );

	addDebugStopFunction( EVT_DEBUG_ADDWILDS_MULTIPLIER_CURTAIN, 
		new DebugStopFunc<ElLuchadorGuts>(this, &ElLuchadorGuts::Debug_AddWilds_Multiplier_Curtain) );

	
}

void ElLuchadorGuts::Debug_AddWilds( SlotSpin *spin )
{
	bDebugAddWilds = true;
	bDebugMultiplier = false;
	bDebugCurtain = false;
}

void ElLuchadorGuts::Debug_Multiplier( SlotSpin *spin )
{
	bDebugAddWilds = false;
	bDebugMultiplier = true;
	bDebugCurtain = false;
}

void ElLuchadorGuts::Debug_Curtain( SlotSpin *spin )
{
	bDebugAddWilds = false;
	bDebugMultiplier = false;
	bDebugCurtain = true;
}

void ElLuchadorGuts::Debug_AddWilds_Multiplier( SlotSpin *spin )
{
	bDebugAddWilds = true;
	bDebugMultiplier = true;
	bDebugCurtain = false;
}

void ElLuchadorGuts::Debug_AddWilds_Curtain( SlotSpin *spin )
{
	bDebugAddWilds = true;
	bDebugMultiplier = false;
	bDebugCurtain = true;
}

void ElLuchadorGuts::Debug_Multiplier_Curtain( SlotSpin *spin )
{
	bDebugAddWilds = false;
	bDebugMultiplier = true;
	bDebugCurtain = true;
}

void ElLuchadorGuts::Debug_AddWilds_Multiplier_Curtain( SlotSpin *spin )
{
	bDebugAddWilds = true;
	bDebugMultiplier = true;
	bDebugCurtain = true;
}